﻿namespace FolderSync
{
    using System;
    using System.IO;
    using System.Linq;
    public class SyncStats
    {
        private enum OperationKind
        {
            None,
            Copy,
            Rename,
            Delete,
            Folders,
        }
        public int TotalFiles, TotalFolders, DeletedFiles, CopiedFiles, CheckedFiles, CheckedFolders;
        public long TotalBytes, DeletedBytes, CopiedBytes;
        public DateTime StartTime = DateTime.Now;
        private DateTime _startOperationTime;
        private OperationKind _operationKind;
        public TimeSpan ElapseCopy, ElapseRename, ElapseDelete, ElapseFolders;
        private float Rate(long c, TimeSpan elapse)
        {
            return ((float)c) / (float)elapse.TotalSeconds;
        }
        public float RateTotalFiles { get { return Rate(TotalFiles, ElapseCopy + ElapseRename + ElapseDelete); } }
        public float RateTotalFolders { get { return Rate(TotalFolders, ElapseFolders); } }
        public float RateDeletedFiles { get { return Rate(DeletedFiles, ElapseDelete); } }
        public float RateCopiedFiles { get { return Rate(CopiedFiles, ElapseCopy); } }
        public float RateTotalBytes { get { return Rate(TotalBytes, ElapseCopy + ElapseRename + ElapseDelete); } }
        public float RateDeletedBytes { get { return Rate(DeletedBytes, ElapseDelete); } }
        public float RateCopiedBytes { get { return Rate(CopiedBytes, ElapseCopy); } }
        /// <summary>
        /// accounts for count of copied files, sum of bytes and starts a timer for elapse
        /// </summary>
        /// <param name="file"></param>
        public bool CountCopiedFile(FileInfo file)
        {
            TotalFiles++;
            CopiedFiles++;
            if (file.Exists)
            {
                CopiedBytes += file.Length;
                TotalBytes += file.Length;
            }
            _operationKind = OperationKind.Copy;
            _startOperationTime = DateTime.Now;
            return true;
        }
        public bool CountMovedFile(FileInfo file)
        {
            TotalFiles++;
            _operationKind = OperationKind.Rename;
            _startOperationTime = DateTime.Now;
            return true;
        }
        public bool CountDeletedFile(FileInfo file)
        {
            TotalFiles++;
            DeletedFiles++;
            if (file.Exists)
            {
                DeletedBytes += file.Length;
                TotalBytes += file.Length;
            }
            _operationKind = OperationKind.Delete;
            _startOperationTime = DateTime.Now;
            return true;
        }
        public bool CountDeletedFile(DirectoryInfo dir)
        {
            dir.GetFiles().All(f => CountDeletedFile(f));
            dir.GetDirectories().All(d => CountDeletedFile(d));
            TotalFolders++;
            _operationKind = OperationKind.Delete;
            return true;
        }
        public bool CountFolder(DirectoryInfo dir)
        {
            _operationKind = OperationKind.Folders;
            _startOperationTime = DateTime.Now;
            TotalFolders++;
            return true;
        }
        public bool CountCheckedFiles(FileInfo file)
        {
            CheckedFiles++;
            return true;
        }
        public bool CountCheckedFolder(DirectoryInfo dir)
        {
            CheckedFolders++;
            return true;
        }
        /// <summary>
        /// stop counting elapse time
        /// </summary>
        public void Done()
        {
            TotalFolders++;
            switch (_operationKind)
            {
                case OperationKind.Copy: ElapseCopy += (DateTime.Now - _startOperationTime); break;
                case OperationKind.Rename: ElapseRename += (DateTime.Now - _startOperationTime); break;
                case OperationKind.Delete: ElapseDelete += (DateTime.Now - _startOperationTime); break;
                case OperationKind.Folders: ElapseFolders += (DateTime.Now - _startOperationTime); break;
                default: break;
            }
            _operationKind = OperationKind.None;
        }
        public static SyncStats operator +(SyncStats l, SyncStats r)
        {
            var ret = new SyncStats();
            try { ret.TotalFiles = l.TotalFiles + r.TotalFiles; }
            catch { };
            try { ret.DeletedFiles = l.DeletedFiles + r.DeletedFiles; }
            catch { };
            try { ret.TotalFolders = l.TotalFolders + r.TotalFolders; }
            catch { };
            try { ret.CopiedFiles = l.CopiedFiles + r.CopiedFiles; }
            catch { };
            try { ret.TotalBytes = l.TotalBytes + r.TotalBytes; }
            catch { };
            try { ret.DeletedBytes = l.DeletedBytes + r.DeletedBytes; }
            catch { };
            try { ret.CopiedBytes = l.CopiedBytes + r.CopiedBytes; }
            catch { };
            try { ret.ElapseCopy = l.ElapseCopy + r.ElapseCopy; }
            catch { };
            try { ret.ElapseRename = l.ElapseRename + r.ElapseRename; }
            catch { };
            try { ret.ElapseDelete = l.ElapseDelete + r.ElapseDelete; }
            catch { };
            try { ret.ElapseFolders = l.ElapseFolders + r.ElapseFolders; }
            catch { };
            try { ret.CheckedFiles = l.CheckedFiles + r.CheckedFiles; }
            catch { };
            try { ret.CheckedFolders = l.CheckedFolders + r.CheckedFolders; }
            catch { };
            try { ret.StartTime = l.StartTime < r.StartTime ? l.StartTime : r.StartTime; }
            catch { };
            return ret;
        }
        public static SyncStats operator -(SyncStats l, SyncStats r)
        {
            return new SyncStats()
            {
                TotalFiles = l.TotalFiles - r.TotalFiles,
                DeletedFiles = l.DeletedFiles - r.DeletedFiles,
                TotalFolders = l.TotalFolders - r.TotalFolders,
                CopiedFiles = l.CopiedFiles - r.CopiedFiles,
                TotalBytes = l.TotalBytes - r.TotalBytes,
                DeletedBytes = l.DeletedBytes - r.DeletedBytes,
                CopiedBytes = l.CopiedBytes - r.CopiedBytes,
                CheckedFiles = l.CheckedFiles - r.CheckedFiles,
                CheckedFolders = l.CheckedFolders - r.CheckedFolders,
                StartTime = l.StartTime < r.StartTime ? l.StartTime : r.StartTime,
            };
        }
        public override string ToString()
        {
            return ToString(false);
        }
        public string ToString(bool verbose = false)
        {
            return string.Format(@"
Copied  files  : {0,6}, {1,8} @{2,8}/s
Deleted files  : {3,6}, {4,8} @{5,8}/s
Moved   files  : {6,6}
Total   files  : {9,6}, {10,8} @{11,8}/s
Handled folders: {12,6}
Checked files  : {13,6}, folders: {14,6}",
                CopiedFiles, CopiedBytes.FormatByteSize(), RateCopiedBytes.FormatByteSize(),
                DeletedFiles, DeletedBytes.FormatByteSize(), RateDeletedBytes.FormatByteSize(),
                TotalFiles - CopiedFiles - DeletedFiles, 0, 0,
                TotalFiles, TotalBytes.FormatByteSize(), RateTotalBytes.FormatByteSize(),
                TotalFolders, CheckedFiles, CheckedFolders
);
        }
    }
    public static class StringExtentionsForStats
    {
        public static string FormatByteSize(this long s)
        {
            return ((float)s).FormatByteSize();
        }
        public static string FormatByteSize(this float s)
        {
            float range = 1;
            if (float.IsInfinity(s) || float.IsNaN(s)) return "******MB";
            if (s < (range *= 1024)) return string.Format("{0,6} B", s);
            if (s < (range *= 1024)) return string.Format("{0:0.0,6}kB", s * 1024 / range);
            if (s < (range *= 1024)) return string.Format("{0:0.0,6}MB", s * 1024 / range);
            if (s < (range *= 1024)) return string.Format("{0:0.0,6}GB", s * 1024 / range);
            return string.Format("{0:0.0,6}TB", s / range);
        }
        public static string PreFormat(this string s)
        {
            return s.Replace("{", "{{").Replace("}", "}}");
        }
    }

}
